﻿using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using FFmpegVideoClip.Configuration;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;

namespace FFmpegVideoClip.Lib
{
    public class ScriptInvoke
    {
        private Func<double, string, double>? func;
        public ScriptInvoke(Func<double, string, double> func) => this.func = func;
        public void Release() => func = null;
        public double Invoke(double TotalSeconds, string Value) { if (func is null) throw new ObjectDisposedException("对象以释放"); return func(TotalSeconds, Value); }
    }
    public class ScriptManage
    {

        public static AssemblyLoadContext? _AssemblyLoadContext = null;
        public static FrozenDictionary<int, ScriptInvoke>? CallProgressScriptDic;

        public static void CompilationScript()
        {
            StringBuilder sb = new();
            sb.AppendLine("using System;");
            sb.AppendLine("public class ProgressScript");
            sb.AppendLine("{");
            foreach (CallProcessor item in Config.Instance.Processors)
            {
                if (string.IsNullOrWhiteSpace(item.ProgressScript))
                    continue;

                sb.AppendLine($"public static double {item.IDName}(double TotalSeconds, string Value)");
                sb.AppendLine("{");
                sb.AppendLine(item.ProgressScript);
                sb.AppendLine("}");
            }
            sb.AppendLine("}");
            // 创建语法树
            SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(sb.ToString());
            // 编译
            CSharpCompilation compilation = CSharpCompilation.Create("ProgressScriptAssembly", [syntaxTree],
                [MetadataReference.CreateFromFile(typeof(object).Assembly.Location)],
                new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

            Assembly assembly;
            using (MemoryStream ms = new MemoryStream())
            {
                var result = compilation.Emit(ms);

                if (!result.Success)
                    throw new ScriptCompilationException($"编译错误:{string.Join(", ", result.Diagnostics.Select(d => d.GetMessage()))}");

                if (_AssemblyLoadContext != null)
                    ClearScripts();
                _AssemblyLoadContext = new("ProgressScript", true);

                //加载程序集
                ms.Position = 0;
                assembly = _AssemblyLoadContext.LoadFromStream(ms);
            }

            //获取类型和方法
            Type type = assembly.GetType("ProgressScript")!;
            MethodInfo[] mediaInfos = type.GetMethods(BindingFlags.Public | BindingFlags.Static);
            Dictionary<int, ScriptInvoke> funcs = new();
            foreach (MethodInfo mediaInfo in mediaInfos)
            {
                //创建委托
                Func<double, string, double> func = (Func<double, string, double>)Delegate.CreateDelegate(typeof(Func<double, string, double>), mediaInfo);
                if (int.TryParse(mediaInfo.Name.Trim().Replace("ID", string.Empty), out int ID))
                    funcs.Add(ID, new(func));
            }

            CallProgressScriptDic = funcs.ToFrozenDictionary();
        }

        public static void ClearScripts()
        {
            if (CallProgressScriptDic != null)
            {
                foreach (ScriptInvoke item in CallProgressScriptDic.Values)
                    item.Release();
                CallProgressScriptDic = null;
            }
            _AssemblyLoadContext?.Unload();
            _AssemblyLoadContext = null;
        }

        [System.Serializable]
        public class ScriptCompilationException : System.Exception
        {
            public ScriptCompilationException() { }
            public ScriptCompilationException(string message) : base(message) { }
            public ScriptCompilationException(string message, System.Exception inner) : base(message, inner) { }
        }
    }
}